﻿using Gasgoo.ShareLibrary.Framework.iQueueProvider;
using Orleans;
using Orleans.Concurrency;
using Orleans.Streams;
using System;
using System.Threading.Tasks;

namespace Gasgoo.ShareLibrary.Framework.ServiceDefine
{
    [StatelessWorker(100)]
    public abstract class GasgooServiceBase : Grain
    {
        public IQueueProducer<T> GetProducerQueueHandler<T>(string topic, string streamNamespace, string providerName = "GasgooSimpleStream")
        {
            IQueueProvider provider = new DefaultQueueProvider(GetStreamProvider(providerName));
            return new QueueProducerHandler<T>(provider, topic, streamNamespace);
        }

        protected virtual string GetKeyToString()
        {
            //string stringKey =  this.GrainReference.PrimaryKeyAsString();
            //return stringKey;

            Guid guidKey = GetGuidKey(out string keyExt);
            if (guidKey != default)
            {
                return string.IsNullOrWhiteSpace(keyExt) ? guidKey.ToString() : $"{guidKey}_{keyExt}";
            }

            string stringKey = GetStringKey();
            if (!string.IsNullOrWhiteSpace(stringKey))
            {
                return stringKey;
            }

            long longKey = GetLongKey(out keyExt);
            return string.IsNullOrWhiteSpace(keyExt) ? longKey.ToString() : $"{longKey}_{keyExt}";

        }
        protected Guid GetGuidKey(out string keyExt) => this.GetPrimaryKey(out keyExt);
        protected long GetLongKey(out string keyExt) => this.GetPrimaryKeyLong(out keyExt);
        protected Guid GetGuidKey() => this.GetPrimaryKey();
        protected long GetLongKey() => this.GetPrimaryKeyLong();
        protected string GetStringKey() => this.GetPrimaryKeyString();

        protected T GetService<T>(Guid primaryKey) where T : GasgooServiceWithGuidKey
        {
            return GrainFactory.GetGrain<T>(primaryKey);
        }
        protected T GetService<T>(long primaryKey) where T : GasgooServiceWithIntegerKey
        {
            if (primaryKey == 0)
            {
                throw new Exception($"primary Key cannot is 0, error");
            }
            return GrainFactory.GetGrain<T>(primaryKey);
        }
        protected T GetService<T>(string primaryKey) where T : GasgooServiceWithStringKey
        {
            return GrainFactory.GetGrain<T>(primaryKey);
        }
        protected T GetService<T>(long primaryKey1, string primaryKey2) where T : GasgooServiceWithIntegerCompoundKey
        {
            if (primaryKey1 == 0)
            {
                throw new Exception($"primary Key cannot is 0, error");
            }
            return GrainFactory.GetGrain<T>(primaryKey1, primaryKey2);
        }
        protected T GetService<T>(Guid primaryKey1, string primaryKey2) where T : GasgooServiceWithGuidCompoundKey
        {
            return GrainFactory.GetGrain<T>(primaryKey1, primaryKey2);
        }
        protected T GetService<T>() where T : GasgooService
        {
            return GrainFactory.GetGrain<T>(0);
        }

        /// <summary>
        /// 计划释放
        /// </summary>
        /// <param name="delayTimeSpan">延迟释放，-timer 表示取消 dispose, timer = null 在调用完成之后释放 </param>
        protected void Dispose(TimeSpan? delayTimeSpan = null)
        {
            if (delayTimeSpan == null)
            {
                DeactivateOnIdle();
            }
            else
            {
                DelayDeactivation((TimeSpan)delayTimeSpan);
            }
        }
    }

    public abstract class GasgooServiceBase<TState> : Grain<TState>
    {
        public QueueProducerHandler<T> GetProducerQueueHandler<T>(string topic, string streamNamespace, string providerName = "GasgooSimpleStream")
        {
            IQueueProvider provider = new DefaultQueueProvider(GetStreamProvider(providerName));
            return new QueueProducerHandler<T>(provider, topic, streamNamespace);
        }

        protected virtual string GetKeyToString()
        {
            //string stringKey = this.GrainReference.PrimaryKeyAsString();
            //return stringKey;

            Guid guidKey = GetGuidKey(out string keyExt);
            if (guidKey != default)
            {
                return string.IsNullOrWhiteSpace(keyExt) ? guidKey.ToString() : $"{guidKey}_{keyExt}";
            }

            string stringKey = GetStringKey();
            if (!string.IsNullOrWhiteSpace(stringKey))
            {
                return stringKey;
            }

            long longKey = GetLongKey(out keyExt);
            return string.IsNullOrWhiteSpace(keyExt) ? longKey.ToString() : $"{longKey}_{keyExt}";

        }
        protected Guid GetGuidKey(out string keyExt) => this.GetPrimaryKey(out keyExt);
        protected long GetLongKey(out string keyExt) => this.GetPrimaryKeyLong(out keyExt);
        protected Guid GetGuidKey() => this.GetPrimaryKey();
        protected long GetLongKey() => this.GetPrimaryKeyLong();
        protected string GetStringKey() => this.GetPrimaryKeyString();

        protected T GetService<T>(Guid primaryKey) where T : GasgooServiceWithGuidKey
        {
            return GrainFactory.GetGrain<T>(primaryKey);
        }
        protected T GetService<T>(long primaryKey) where T : GasgooServiceWithIntegerKey
        {
            if (primaryKey == 0)
            {
                throw new Exception($"primary Key cannot is 0, error");
            }
            return GrainFactory.GetGrain<T>(primaryKey);
        }
        protected T GetService<T>(string primaryKey) where T : GasgooServiceWithStringKey
        {
            return GrainFactory.GetGrain<T>(primaryKey);
        }
        protected T GetService<T>(long primaryKey1, string primaryKey2) where T : GasgooServiceWithIntegerCompoundKey
        {
            if (primaryKey1 == 0)
            {
                throw new Exception($"primary Key cannot is 0, error");
            }
            return GrainFactory.GetGrain<T>(primaryKey1, primaryKey2);
        }
        protected T GetService<T>(Guid primaryKey1, string primaryKey2) where T : GasgooServiceWithGuidCompoundKey
        {
            return GrainFactory.GetGrain<T>(primaryKey1, primaryKey2);
        }
        protected T GetService<T>() where T : GasgooService
        {
            return GrainFactory.GetGrain<T>(0);
        }

        /// <summary>
        /// 计划释放
        /// </summary>
        /// <param name="delayTimeSpan">延迟释放，-timer 表示取消 dispose, timer = null 在调用完成之后释放 </param>
        protected void Dispose(TimeSpan? delayTimeSpan = null)
        {
            if (delayTimeSpan == null)
            {
                DeactivateOnIdle();
            }
            else
            {
                DelayDeactivation((TimeSpan)delayTimeSpan);
            }
        }

    }

    public abstract class GasgooServiceStorageBase<TState> : Grain<TState>
    {
        public QueueProducerHandler<T> GetProducerQueueHandler<T>(string topic, string streamNamespace, string providerName = "GasgooSimpleStream")
        {
            IQueueProvider provider = new DefaultQueueProvider(GetStreamProvider(providerName));
            return new QueueProducerHandler<T>(provider, topic, streamNamespace);
        }

        protected virtual string GetKeyToString()
        {
            //string stringKey =  this.GrainReference.PrimaryKeyAsString();
            //return stringKey;

            Guid guidKey = GetGuidKey(out string keyExt);
            if (guidKey != default)
            {
                return string.IsNullOrWhiteSpace(keyExt) ? guidKey.ToString() : $"{guidKey}_{keyExt}";
            }

            string stringKey = GetStringKey();
            if (!string.IsNullOrWhiteSpace(stringKey))
            {
                return stringKey;
            }

            long longKey = GetLongKey(out keyExt);
            return string.IsNullOrWhiteSpace(keyExt) ? longKey.ToString() : $"{longKey}_{keyExt}";

        }
        protected Guid GetGuidKey(out string keyExt) => this.GetPrimaryKey(out keyExt);
        protected long GetLongKey(out string keyExt) => this.GetPrimaryKeyLong(out keyExt);
        protected Guid GetGuidKey() => this.GetPrimaryKey();
        protected long GetLongKey() => this.GetPrimaryKeyLong();
        protected string GetStringKey() => this.GetPrimaryKeyString();

        protected T GetService<T>(Guid primaryKey) where T : GasgooServiceWithGuidKey
        {
            return GrainFactory.GetGrain<T>(primaryKey);
        }
        protected T GetService<T>(long primaryKey) where T : GasgooServiceWithIntegerKey
        {
            if (primaryKey == 0)
            {
                throw new Exception($"primary Key cannot is 0, error");
            }
            return GrainFactory.GetGrain<T>(primaryKey);
        }
        protected T GetService<T>(string primaryKey) where T : GasgooServiceWithStringKey
        {
            return GrainFactory.GetGrain<T>(primaryKey);
        }
        protected T GetService<T>(long primaryKey1, string primaryKey2) where T : GasgooServiceWithIntegerCompoundKey
        {
            if (primaryKey1 == 0)
            {
                throw new Exception($"primary Key cannot is 0, error");
            }
            return GrainFactory.GetGrain<T>(primaryKey1, primaryKey2);
        }
        protected T GetService<T>(Guid primaryKey1, string primaryKey2) where T : GasgooServiceWithGuidCompoundKey
        {
            return GrainFactory.GetGrain<T>(primaryKey1, primaryKey2);
        }
        protected T GetService<T>() where T : GasgooService
        {
            return GrainFactory.GetGrain<T>(0);
        }

        /// <summary>
        /// 计划释放
        /// </summary>
        /// <param name="delayTimeSpan">延迟释放，-timer 表示取消 dispose, timer = null 在调用完成之后释放 </param>
        protected void Dispose(TimeSpan? delayTimeSpan = null)
        {
            if (delayTimeSpan == null)
            {
                DeactivateOnIdle();
            }
            else
            {
                DelayDeactivation((TimeSpan)delayTimeSpan);
            }
        }

        protected new abstract Task ClearStateAsync();
        protected new abstract Task ReadStateAsync();
        protected new abstract Task WriteStateAsync();
        public override async Task OnActivateAsync()
        {
            await ReadStateAsync();
        }
    }


    [ImplicitStreamSubscription]
    public abstract class GasgooSubscriberServiceBase<T> : Grain, IAsyncObserver<T>
    {
#pragma warning disable CS8618 // 在退出构造函数时，不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。
        protected StreamSubscriptionHandle<T>? streamHandle;
#pragma warning restore CS8618 // 在退出构造函数时，不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。

        public GasgooSubscriberServiceBase(string streamProvider, string streamNamespace)
        {
            StreamProvider = streamProvider;
            StreamNamespace = streamNamespace;
        }

        public string StreamProvider { get; }
        public string StreamNamespace { get; }

        public override async Task OnActivateAsync()
        {
            var streamId = this.GetPrimaryKey();
            var streamProvider = this.GetStreamProvider(StreamProvider);
            var stream = streamProvider.GetStream<T>(streamId, StreamNamespace);
            streamHandle = await stream.SubscribeAsync(this);
        }
        public abstract Task OnCompletedAsync();

        public abstract Task OnErrorAsync(Exception ex);

        public abstract Task OnNextAsync(T item, StreamSequenceToken token);
    }
}
